import * as Cesium from "cesium";
import * as dat from "dat.gui";

export default class FogEffect {
  constructor(viewer, options) {
    if (!viewer) throw new Error("no viewer object!");
    options = options || {};
    // 雾的浓度
    this.visibility = Cesium.defaultValue(options.visibility, 0.5);
    // 雾的颜色
    this.color = Cesium.defaultValue(
      options.color,
      Cesium.Color.WHITE
    );
    // 雾的最小距离
    this.minDistance = Cesium.defaultValue(options.minDistance, 10000.0);
    // 雾的最大距离，最终效果为在雾的最小距离和最大距离之间的插值
    this.maxDistance = Cesium.defaultValue(options.maxDistance, 1000000.0);
    this._show = Cesium.defaultValue(options.show, !0);
    this.viewer = viewer;
    this.init();
  }

  init() {
    this.fogStage = new Cesium.PostProcessStage({
      name: "czm_fog",
      fragmentShader: this.fog(),
      uniforms: {
        visibility: () => {
          return this.visibility;
        },
        fogColor: () => {
          return this.color;
        },
        minDistance: () => {
          return this.minDistance;
        },
        maxDistance: () => {
          return this.maxDistance;
        },
      },
    });
    this.addGui(this.fogStage.uniforms);
    this.viewer.scene.postProcessStages.add(this.fogStage);
  }

  /**
   * 添加Gui控制面板
   */
  addGui(uniformMap) {
    const gui = new dat.GUI();
    console.log("uniformMap", uniformMap);
    gui
      .add(this, "visibility")
      .name("雾的浓度")
      .min(0.0)
      .max(1.0)
      .step(0.1)
      .onChange((v) => {
        uniformMap.visibility = () => v;
      });

    gui
      .add(this, "minDistance")
      .name("最小距离")
      .min(0)
      .max(100000.0)
      .step(100)
      .onChange((v) => {
        uniformMap.minDistance = () => v;
      });

    gui
      .add(this, "maxDistance")
      .name("最大距离")
      .min(100000.0)
      .max(2000000.0)
      .step(100)
      .onChange((v) => {
        uniformMap.maxDistance = () => v;
      });
  }

  destroy() {
    if (!this.viewer || !this.fogStage) return;
    this.viewer.scene.postProcessStages.remove(this.fogStage);
    delete this.visibility;
    delete this.color;
    delete this.minDistance;
    delete this.maxDistance;
  }

  show(visible) {
    this._show = visible;
    this.fogStage.enabled = this._show;
  }

  fog() {
    return `
      precision mediump float;
      uniform sampler2D colorTexture;
      uniform sampler2D depthTexture;
      uniform float visibility;
      uniform vec4 fogColor;
      uniform float minDistance;
      uniform float maxDistance;
      in vec2 v_textureCoordinates;
      out vec4 myColor;

      // 将深度纹理中的非线性深度值转换为线性空间中的视线距离，即像素到摄像机的实际距离
      float getDistance(sampler2D depthTexture, vec2 texCoords)
      {
          float depth = czm_unpackDepth(texture(depthTexture, texCoords));
          if (depth == 0.0) {
              return czm_infinity;
          }
          vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
          return -eyeCoordinate.z / eyeCoordinate.w;
      }

      // 根据距离在两个阈值间线性插值雾的强度，近处无雾，远处全雾
      float interpolateByDistance(float minDistance, float maxDistance, float distance)
      {
          float startDistance = minDistance;
          float startValue = 0.0;
          float endDistance = maxDistance;
          float endValue = 1.0;
          float t = clamp((distance - startDistance) / (endDistance - startDistance), 0.0, 1.0);
          return mix(startValue, endValue, t);
      }

      // 实现基于透明度的颜色混合
      vec4 alphaBlend(vec4 sourceColor, vec4 destinationColor)
      {
          return sourceColor * vec4(sourceColor.aaa, 1.0) + destinationColor * (1.0 - sourceColor.a);
      }

      void main(void)
      {
         vec4 origncolor = texture(colorTexture, v_textureCoordinates);
         float distance = getDistance(depthTexture, v_textureCoordinates);
         float f = interpolateByDistance(minDistance, maxDistance, distance) * visibility;
         // myColor = mix(origncolor, fogColor, f);
         vec4 finalFogColor = vec4(fogColor.rgb, fogColor.a * f);
         myColor = alphaBlend(finalFogColor, origncolor);
      }
    `;
  }
}
